module Hazards_ctrl (
  input clk,
  input rst,
  
  output [4:0] enable/*verilator public_flat*/,
  output [4:0] flush/*verilator public_flat*/,
  input  [4:0] ready/*verilator public_flat*/,
  input  [4:0] busy/*verilator public_flat*/,

  /* The data_valid signal is for difftest. */
  output [4:0] data_valid,

  input [4:0] id_rs1,
  input [4:0] id_rs2,
  input [4:0] id_dst,
  input [4:0] exe_dst,
  input [4:0] mem_dst,
  input [4:0] wb_dst,

  /* These data come from pipeline regs. */
  input [4:0] id_rs1_r,
  input [4:0] id_rs2_r,
  input [63:0] id_src1_r,
  input [63:0] id_src2_r,

  input exe_dst_valid_r,
  input exe_data_valid_r,
  input [4:0] exe_dst_r,
  input [63:0] exe_data_r,
  input mem_data_valid_r,
  input [4:0] mem_dst_r,
  input [63:0] mem_data_r,
  /* This data comes from writenack unit. */
  input wb_en_i,
  input [4:0] wb_dst_i,
  input [63:0] wb_data_i,

  /* These data go to execute unit. */
  output reg [63:0] id_src1_o,
  output reg [63:0] id_src2_o,



  input branch_miss,
  input icache_busy,
  input dcache_busy,

  input id_rs1_valid,
  input id_rs2_valid,
  input id_dst_valid

);

  reg [4:0] wb_dst_r;
  reg [63:0] wb_data_r;
  always @(posedge clk) begin
    if(rst) begin
      wb_dst_r <= 5'b0;
      wb_data_r <= 64'b0;
    end if(wb_en_i) begin
      wb_dst_r <= wb_dst_i;
      wb_data_r <= wb_data_i;
    end
  end

  wire src1_zero = id_rs1_r == 5'b0;
  wire src2_zero = id_rs2_r == 5'b0;

  wire [4:2] src1_conflict;
  wire [4:2] src2_conflict;
  
  
  /* Judge whether has data conflict. */
  assign src1_conflict[2] = enable_r[3]&~src1_zero&exe_dst_valid_r&(id_rs1_r == exe_dst_r);
  assign src1_conflict[3] = enable_r[4]&~src1_zero&mem_data_valid_r&(id_rs1_r == mem_dst_r);
  assign src1_conflict[4] = ~src1_zero&(id_rs1_r == wb_dst_r);

  assign src2_conflict[2] = enable_r[3]&~src2_zero&exe_dst_valid_r&(id_rs2_r == exe_dst_r);
  assign src2_conflict[3] = enable_r[4]&~src2_zero&mem_data_valid_r&(id_rs2_r == mem_dst_r);
  assign src2_conflict[4] = ~src2_zero&(id_rs2_r == wb_dst_r);
  
  reg [1:0] data_relayed;
  wire src1_relay_conflict = ((|src1_conflict)&~data_relayed[0]);
  wire src2_relay_conflict = ((|src2_conflict)&~data_relayed[1]);
  reg [4:0] pause;
  localparam FSM_IDLE = 2'b00;
  localparam FSM_CONF = 2'b01;/* FSM_CONF means that the data conflict can't use data-relay to solve. */
  reg [1:0] state;
  always @(posedge clk) begin
    if(rst) state <= FSM_IDLE;
    else begin
      case(state)
      FSM_IDLE: if(relay_conflict|(|busy[4:2])) state <= FSM_CONF;
      FSM_CONF: if(~(relay_conflict|(|busy[4:2]))) state <= FSM_IDLE;
      default: ;
      endcase
    end
  end

  reg [63:0] src1,src2;
  reg src1_valid,src2_valid;
  always @(posedge clk) begin
    if(rst) begin
      src1 <= 64'b0;
      src2 <= 64'b0;
    end else begin
      case(state)
      FSM_IDLE: begin
        src1_valid <= ~src1_relay_conflict;
        src2_valid <= ~src2_relay_conflict;
        src1 <= id_src1_o;
        src2 <= id_src2_o; 
      end
      FSM_CONF: begin
        if(~src1_valid) begin
          if(wb_en_i && id_rs1_r == wb_dst_i)
          begin
            src1_valid <= 1'b1;
            src1 <= wb_data_i;
          end
        end
        if(~src2_valid) begin
          if(wb_en_i && id_rs2_r == wb_dst_i)
          begin
            src2_valid <= 1'b1;
            src2 <= wb_data_i;
          end
        end
      end
      default: begin
        src1_valid <= 1'b0;
        src2_valid <= 1'b0;
      end
      endcase
    end
  end

  /* Judge whether data conflict has been relayed. */

  always@(*) begin
    data_relayed = 2'b0;
    case(state)
    FSM_IDLE: begin
      if(src1_zero) begin
        id_src1_o = 64'b0;
        data_relayed[0] = 1'b1;
      end else if(src1_conflict[2]) begin
        if(~exe_data_valid_r) begin
          id_src1_o = 64'b0;
          data_relayed[0] = 1'b0;
        end else begin
          id_src1_o = exe_data_r;
          data_relayed[0] = ~busy[2];     
        end
      end else if(src1_conflict[3]) begin
        id_src1_o = mem_data_r;
        data_relayed[0] = ~busy[3];
      end else if(src1_conflict[4]) begin
        id_src1_o = wb_data_r;
        data_relayed[0] = 1'b1;
      end else begin 
        id_src1_o = id_src1_r;
        data_relayed[0] = 1'b0;
      end
  
      if(src2_zero) begin
        id_src2_o = 64'b0;
        data_relayed[1] = 1'b1;
      end else if(src2_conflict[2]) begin
        if(~exe_data_valid_r) begin
          id_src2_o = 64'b0;
          data_relayed[1] = 1'b0;
        end else begin
          id_src2_o = exe_data_r;
          data_relayed[1] = ~busy[2];     
        end
      end else if(src2_conflict[3]) begin
        id_src2_o = mem_data_r;
        data_relayed[1] = ~busy[3];
      end else if(src2_conflict[4]) begin
        id_src2_o = wb_data_r;
        data_relayed[1] = 1'b1;
      end else begin 
        id_src2_o = id_src2_r;
        data_relayed[1] = 1'b0;
      end
    end
    FSM_CONF: begin
      id_src1_o = src1;
      id_src2_o = src2;
      data_relayed[0] = src1_valid;
      data_relayed[1] = src2_valid;
    end
    default: begin
      id_src1_o = 64'b0;
      id_src2_o = 64'b0;
      data_relayed[0] = 1'b0;
      data_relayed[1] = 1'b0;
    end
    endcase
    
  end


  wire relay_conflict = (src1_relay_conflict|src2_relay_conflict)|((state == FSM_CONF)&(~(&data_relayed)));

  reg [4:0] enable_r,flush_r;
  // /* If decode is conflict or any stage is busy, pause the frontend. */
  // wire pause_frontend = |conflict | |busy;

  /* If any stage is busy, pause the frontend. */
  wire pause_frontend = |busy;

  always @(*) begin
    pause = 5'b0;
    if(pause_frontend) pause[1:0] = ~2'b0;
    if(relay_conflict) pause[2:0] = ~3'b0;
    /* If any backend stage is busy, pause the stage and all stages before it. Besides, make the stage output is invalid. */
    if(busy[2]) pause[3:0] = ~4'b0;
    if(busy[3]) pause[4:0] = ~5'b0;

    flush_r = 5'b0;
    /* If branch predict mistake or other unpredicted branch occur, flush the frontend. */
    if(branch_miss) flush_r[1:0] = ~2'b0;
    
  end

  integer i;
  always @(posedge clk) begin
    if(rst) begin
      enable_r <= 5'b0;
    end else begin
      /* Stage enable input must be zero if the stage before it is pause or flush. */
      enable_r <= {enable_r[3:0]&~(pause[3:0]|flush[3:0]),1'b1};
      /* If a branch predict is mistake, the frontend data will be flush in the next cycle. */
      if(branch_miss) begin
        enable_r[2:1] <= 2'b0;
      end
      /* If a stage is pause and not flush, the stage's data should be kept. */
      for (i = 0; i < 5; i = i + 1 ) begin
        if(pause[i]&~flush[i]) begin
          enable_r[i] <= enable_r[i];
        end
      end
    end
  end

  assign enable = (~pause)&enable_r;
  assign flush = flush_r;
  
  assign data_valid = enable_r&~flush;
endmodule
